home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Plus
/
Graphics Plus.iso
/
general
/
visulztn
/
saoimage
/
saoimage.lha
/
histlist.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-21
|
15KB
|
429 lines
#ifndef lint
static char SccsId[] = "%W% %G%";
#endif
/* Module: histlist.c (Histogram List)
* Subroutine: make_equalized_list() returns: void
* Copyright: 1989 Smithsonian Astrophysical Observatory
* You may do anything you like with this file except remove
* this copyright. The Smithsonian Astrophysical Observatory
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
* Modified: {0} Michael VanHilst initial version 30 May 1989
* {1} MVH constraints based on best history 15 Dec 1989
* {2} JRW limit to get out of infinite loop 19 Jun 1991
* {n} <who> -- <does what> -- <when>
*/
#include <stdio.h>
#include "hfiles/histeq.h" /* define SubrangeList */
#define MAXITERATIONS 1000 /* --jrw */
#ifdef DEBUG
#define HDEBUG
#endif
/*
* Subroutine: make_equalized_list
* Purpose: Distributing levels for a subrange section of the histogram
*/
void make_equalized_list ( histogram, list, low_entry, high_entry,
pixel_area, color_levels )
int *histogram;
SubrangeList *list;
int low_entry, high_entry;
int pixel_area, color_levels;
{
int shrink_level, stretch_level;
int max_shrink, min_stretch;
int average_area, end_area, min_area, max_area;
int levels_given; /* levels which equalize_simply used */
int correction; /* correction for asymptotic convergence */
int iterations; /* avoid infinite loop --jrw */
int end_error; /* deviation from ideal of last level */
int best_levels_over; /* levels from an equalize worth remembering */
int best_levels_under;
int best_average_over = 0; /* average_area used to get best_levels */
int best_average_under = 0;
static int equalize_simply();
#ifdef JIGGLE
static void adjust_list();
#endif
/* else allocation distribution must first be determined */
/* run through histgram section making basic allocation and taking notes */
average_area = pixel_area / color_levels;
max_area = max_shrink = 0;
min_area = min_stretch = pixel_area + 1;
/* use 2*color_levels to allow allocation to exceed limit */
levels_given =
equalize_simply(histogram, list, 0, average_area,
low_entry, high_entry, color_levels + color_levels,
&shrink_level, &stretch_level,
&end_area, &min_area, &max_area,
&min_stretch, &max_shrink);
if( levels_given != color_levels ) {
/* make approximation for large errors */
correction = ((color_levels - levels_given) * average_area) / -100;
/* default correction if error was relatively small */
if( (correction < 2) && (correction > -2) ) {
if( color_levels > levels_given )
correction = -2;
else
correction = 2;
}
/* record what has been learned so far */
if( levels_given > color_levels ) {
best_levels_over = levels_given;
best_average_over = average_area;
} else {
best_levels_under = levels_given;
best_average_under = average_area;
}
} else
/* if first try was good enough, go with it */
correction = 0;
end_error = 0;
iterations = 0; /* --jrw */
while( (correction != 0) && (iterations++ < MAXITERATIONS) ) { /* --jrw */
average_area += correction;
levels_given =
equalize_simply(histogram, list, 0, average_area,
low_entry, high_entry, color_levels + color_levels,
&shrink_level, &stretch_level,
&end_area, &min_area, &max_area,
&min_stretch, &max_shrink);
if( levels_given != color_levels ) {
if( levels_given > color_levels ) {
/* is this the closest overshoot */
if( (best_average_over == 0) || (levels_given < best_levels_over) ) {
best_levels_over = levels_given;
best_average_over = average_area;
}
if( correction < 0 ) {
/* went too far, turn back if that is an option */
if( (correction == -1) && (end_error == 0) )
/* done if we never got to a true result */
correction = 0;
else
/* asymtotic correction to converge on solution */
correction = (correction - 1) / -2;
} else if( (best_average_under != 0) &&
(best_average_under <= (average_area + correction)) )
/* if continue in same direction, don't pass previous best */
correction = (best_average_under - average_area) - 1;
} else {
/* is this the closest undershoot */
if( (best_average_under == 0) || (levels_given < best_levels_under) ) {
best_levels_under = levels_given;
best_average_under = average_area;
}
if( correction > 0 ) {
if( (correction == 1) && (end_error == 0) )
/* done if we never got to a true result */
correction = 0;
else
/* asymtotic correction to converge on solution */
correction = (correction + 1) / -2;
} else if( (best_average_over != 0) &&
(best_average_over <= (average_area + correction)) )
/* if continue in same direction, don't pass previous best */
correction = (best_average_over - average_area) + 1;
}
} else if( (end_area > max_area) || (end_area < min_area) ) {
if( correction < -16 )
correction = -16;
else if( correction > 16 )
correction = 16;
if( end_area > average_area ) {
if( correction < 0 ) {
/* this is an overshoot */
correction = correction / -2;
if( (end_error == 0) || ((end_area - average_area) > end_error) ) {
/* go back if we might do better */
if( correction == 0 )
correction = 1;
}
end_error = end_area - average_area;
}
} else {
if( correction > 0 ) {
/* this is an overshoot */
correction = correction / -2;
if( (end_error == 0) || ((average_area - end_area) > end_error) ) {
/* go back if we might do better */
if( correction == 0 )
correction = -1;
}
end_error = average_area - end_area;
}
}
} else
correction = 0;
}
if (correction != 0) {
(void)printf("SAOimage: histeq could not converge on best distribution,");
(void)printf(" proceeding anyway\n");
(void)fflush(stdout);
}
/* if no fit yet, force one by making last level stretch */
if( (levels_given != color_levels) && (best_average_over != 0) ) {
levels_given =
equalize_simply(histogram, list, 0, best_average_over,
low_entry, high_entry, color_levels,
&shrink_level, &stretch_level,
&end_area, &min_area, &max_area,
&min_stretch, &max_shrink);
}
#ifdef JIGGLE
/* This section to directly adjust level boundaries for a better fit
* is experimental. It is not currently used, because the improvement
* is not important relative to the processing time that can occur
* in non-stable situations. In some situations, such as when
* levels != color_levels coming out of the previous loop, a few
* passes could yield worthwhile improvements.
*/
#ifdef HDEBUG
(void)printf("Before adjustment: %d (between: %d %d), end: %d\n",
max_area - min_area, min_area, max_area, end_area);
(void)fflush(stdout);
#endif
/* jiggle the allocations for a better fit (reduce maximum error) */
if( (end_area > max_area) && (end_area > min_stretch) ) {
/* bunch toward the end to make it smaller */
while( (end_area > max_area) && (end_area > min_stretch) ) {
average_area = min_stretch;
adjust_list(1, histogram, list, color_levels,
low_entry, high_entry, average_area,
&stretch_level, &shrink_level, &end_area,
&min_area, &max_area, &min_stretch, &max_shrink);
}
} else if( (end_area < min_area) && (end_area < max_shrink) ) {
/* bunch away from the end to make it bigger */
while( (end_area < min_area) && (end_area < max_shrink) ) {
average_area = max_shrink;
adjust_list(0, histogram, list, color_levels, low_entry, high_entry,
average_area, &stretch_level, &shrink_level, &end_area,
&min_area, &max_area, &min_stretch, &max_shrink);
}
}
#ifdef HDEBUG
(void)printf("After adjustment: %d (between: %d %d), end: %d\n",
max_area - min_area, min_area, max_area, end_area);
(void)fflush(stdout);
#endif
#endif
}
/*
* Subroutine: equalize_simply
* Purpose: Make a list to describe map using basic allocation method
* Note: Allocate levels from "level" to "color_levels"
*/
static int equalize_simply ( histogram, histlist,
level, average_area, low_entry, high_entry,
color_levels, shrink_level, stretch_level,
end_area, min_area, max_area,
min_stretch, max_shrink )
int *histogram;
SubrangeList *histlist;
int level, average_area;
int low_entry, high_entry;
int color_levels;
int *shrink_level, *stretch_level;
int *end_area, *min_area, *max_area;
int *min_stretch, *max_shrink;
{
int entry, neighbor_entry;
int area, old_area;
int err_low, err_high;
int init_next;
/* initialize parameters */
init_next = 0;
area = 0;
histlist[level].first = low_entry;
/* reduce by one for indexing to end on last list level */
color_levels--;
/* in this pass, assign levels by simple method */
for( entry = low_entry; entry <= high_entry; entry++ ) {
if( init_next ) {
++level;
histlist[level].first = entry;
area = 0;
init_next = 0;
}
old_area = area;
area += histogram[entry];
/* while levels last, scan till exceed average area */
if( (level < color_levels) && (area >= average_area) ) {
err_low = average_area - old_area;
err_high = area - average_area;
if( err_high < err_low ) {
/* err to excess */
histlist[level].last = entry;
histlist[level].pixel_area = area;
/* shrink option would be to exclude this entry */
histlist[level].shrink_area = old_area;
histlist[level].shrink_entry = entry - 1;
/* look for next non-zero entry to include for stretch option */
neighbor_entry = entry;
do {
++neighbor_entry;
} while( (neighbor_entry <= high_entry) &&
(histogram[neighbor_entry] == 0) );
if( neighbor_entry > high_entry ) {
/* if at high end, exagerate to preclude this option */
histlist[level].stretch_area = 10 * area;
histlist[level].stretch_entry = high_entry;
} else {
histlist[level].stretch_area = area + histogram[neighbor_entry];
histlist[level].stretch_entry = neighbor_entry;
}
} else {
/* err short */
/* exclude this entry */
neighbor_entry = entry - 1;
histlist[level].last = neighbor_entry;
histlist[level].pixel_area = old_area;
/* including this entry is the stretch option */
histlist[level].stretch_area = area;
histlist[level].stretch_entry = entry;
/* look for previous non-zero entry for shrink option (check limit) */
while( (neighbor_entry >= low_entry) &&
(histogram[neighbor_entry] == 0) )
--neighbor_entry;
if( neighbor_entry < low_entry ) {
/* if at low end, preclude this option */
histlist[level].shrink_area = 0;
histlist[level].shrink_entry = low_entry;
} else {
histlist[level].shrink_area = old_area - histogram[neighbor_entry];
histlist[level].shrink_entry = neighbor_entry - 1;
}
/* leave this entry for the next level */
--entry;
}
/* check for new champions of excess */
if( histlist[level].pixel_area < *min_area )
*min_area = histlist[level].pixel_area;
if( histlist[level].pixel_area > *max_area )
*max_area = histlist[level].pixel_area;
if( histlist[level].stretch_area <= *min_stretch ) {
*min_stretch = histlist[level].stretch_area;
*stretch_level = level;
}
if( histlist[level].shrink_area >= *max_shrink ) {
*max_shrink = histlist[level].shrink_area;
*shrink_level = level;
}
init_next = 1;
}
}
/* mark end of list (level should be last one (color_levels - 1)) */
histlist[level].pixel_area = area;
*end_area = area;
histlist[level].last = entry - 1;
return( level + 1 );
}
#ifdef JIGGLE
/*
* Subroutine: adjust_list
* Purpose: Alter a level of the map and remake section above altered level
*/
static void adjust_list ( stretch_this_entry, histogram, histlist,
color_levels, low_entry, high_entry,
average_area, stretch_level, shrink_level, end_area,
min_area, max_area, min_stretch, max_shrink )
int stretch_this_entry; /* i: stretch an entry, else shrink one */
int *histogram;
SubrangeList *histlist;
int color_levels;
int low_entry, high_entry, average_area;
int *shrink_level, *stretch_level;
int *end_area, *min_area, *max_area;
int *min_stretch, *max_shrink;
{
int level, j, neighbor_entry;
int equalize_simply();
/* make an adjustment */
if( stretch_this_entry ) {
/* stretch an entry */
level = *stretch_level;
/* shrink returns to what it was */
histlist[level].shrink_area = histlist[level].pixel_area;
histlist[level].shrink_entry = histlist[level].last;
/* current was stretch option before */
histlist[level].pixel_area = histlist[level].stretch_area;
histlist[level].last = histlist[level].stretch_entry;
/* find the next non-zero entry for the new stretch option */
neighbor_entry = histlist[level].last;
do {
++neighbor_entry;
} while( (neighbor_entry <= high_entry) &&
(histogram[neighbor_entry] == 0) );
if( neighbor_entry > high_entry ) {
/* don't go off the edge */
histlist[level].stretch_area *= 10;
histlist[level].stretch_entry = high_entry;
} else {
histlist[level].stretch_area += histogram[neighbor_entry];
histlist[level].stretch_entry = neighbor_entry;
}
} else {
/* shrink an entry */
level = *shrink_level;
/* stretch option becomes what it was */
histlist[level].stretch_area = histlist[level].pixel_area;
histlist[level].stretch_entry = histlist[level].last;
/* current was shrink option before */
histlist[level].last = histlist[level].shrink_entry;
histlist[level].pixel_area = histlist[level].shrink_area;
/* look for last non-zero entry to exclude for new shrink option */
neighbor_entry = histlist[level].last;
while( (neighbor_entry >= low_entry) &&
(histogram[neighbor_entry] == 0) )
--neighbor_entry;
if( neighbor_entry < low_entry ) {
/* don't go off the edge */
histlist[level].shrink_area = 0;
histlist[level].shrink_entry = low_entry;
} else {
histlist[level].shrink_area -= histogram[neighbor_entry];
histlist[level].shrink_entry = neighbor_entry - 1;
}
}
/* remake list with this change */
*min_area = *min_stretch = average_area * 3;
*max_shrink = *max_area = 0;
/* retake notes up to point of change */
for( j = 0; j <= level; j++ ) {
if( histlist[j].pixel_area < *min_area )
*min_area = histlist[j].pixel_area;
if( histlist[j].pixel_area > *max_area )
*max_area = histlist[j].pixel_area;
if( histlist[j].stretch_area <= *min_stretch ) {
*min_stretch = histlist[j].stretch_area;
*stretch_level = j;
}
if( histlist[j].shrink_area >= *max_shrink ) {
*max_shrink = histlist[j].shrink_area;
*shrink_level = j;
}
}
if( (level < color_levels) && (histlist[level].last < high_entry) )
/* make rest of list in normal way */
(void)equalize_simply(histogram, histlist, j, average_area,
histlist[level].last + 1, high_entry, color_levels,
shrink_level, stretch_level,
end_area, min_area, max_area,
min_stretch, max_shrink);
}
#endif